package cn.itcast.zt.schedule;

import cn.itcast.zt.annotation.CronSchedule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @author zhangtian105
 * @ClassName ScheduleManager.java
 * @projectName SpringBoot2.x
 * @Description 定时任务注册管理
 * @createTime 2020年04月27日 17:28:00
 */
@Component
@Slf4j
public class ScheduleManager implements ApplicationContextAware, BeanFactoryAware, BeanPostProcessor, DisposableBean {
    @Autowired
    private ScheduleJobTask scheduleJobTask ;
    private ApplicationContext applicationContext ;
    private BeanFactory beanFactory ;
    private ScheduledTaskRegistrar scheduledTaskRegistrar = new ScheduledTaskRegistrar() ;
    private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(50) ;
    private boolean init = true ;

    public void configTask() {
        Class<?> clazz = scheduleJobTask.getClass() ;
        Method[] methods = clazz.getDeclaredMethods() ;

        for (Method method : methods) {
            CronSchedule cronSchedule = method.getDeclaredAnnotation(CronSchedule.class) ;
            if (Objects.isNull(cronSchedule)) {
                continue;
            }

            String cron = cronSchedule.cron() ;
            if (StringUtils.isEmpty(cron) || StringUtils.isEmpty(ScheduleJobTask.GLOBAL_CRON)) {
                continue;
            }

            Runnable runnable = new ScheduledMethodRunnable(scheduleJobTask, method) ;
            scheduledTaskRegistrar.addTriggerTask(new TriggerTask(runnable, new Trigger() {
                @Override
                public Date nextExecutionTime(TriggerContext triggerContext) {
                    CronTrigger cronTrigger = new CronTrigger(ScheduleJobTask.GLOBAL_CRON) ;
                    Date nextExec = cronTrigger.nextExecutionTime(triggerContext) ;
                    return nextExec;
                }
            }));
        }

        scheduledTaskRegistrar.setScheduler(scheduler);
        scheduledTaskRegistrar.afterPropertiesSet();
    }

    /**
     * 功能描述: <br>
     * 〈reReqiester方法可以暴露出去被其它类调用。当外部逻辑检测到定时任务的执行时间发生变化，调用此方法重新注册定时任务〉
     * @Param: []
     * @Return: void
     * @Author: zhangtian105
     * @Date: 2020/4/27 17:44
     */
    public void reRequester() {
        scheduledTaskRegistrar.destroy();
        scheduledTaskRegistrar.setTriggerTasksList(new ArrayList<>());
        configTask();
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory ;
    }

    @Override
    public void destroy() throws Exception {
        scheduledTaskRegistrar.destroy();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext ;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (init) {
            // 防止多次初始化
            configTask();
            init = false ;
        }

        return bean;
    }
}
